package com.dianzi.certification.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dianzi.certification.dto.UserLoginDTO;
import com.dianzi.certification.dto.UserRegisterDTO;
import com.dianzi.certification.entity.CustomUserDetail;
import com.dianzi.certification.entity.User;
import com.dianzi.certification.entity.UserRole;
import com.dianzi.certification.enums.RoleEnum;
import com.dianzi.certification.mapper.UserMapper;
import com.dianzi.certification.service.UserRoleService;
import com.dianzi.certification.service.UserService;
import com.dianzi.certification.utils.JwtTokenUtils;
import com.dianzi.certification.vo.R;
import com.dianzi.certification.vo.UserLoginVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Objects;

import static com.dianzi.certification.constants.LoginTypeConstant.EMAIL;
import static com.dianzi.certification.constants.LoginTypeConstant.PHONE;
import static com.dianzi.certification.constants.ResponseConstant.STATUS_FAIL;


@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private JwtTokenUtils jwtTokenUtils;

    @Resource
    private UserRoleService userRoleService;

    @Override
    public R<User> findByName(String username) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper();
        wrapper.eq(StringUtils.isNotBlank(username), User::getName, username);
        User user = userMapper.selectOne(wrapper);
        if (Objects.isNull(user)) {
            return R.error();
        }
        return R.success(user);
    }

    @Override
    public R<UserLoginVO> login(UserLoginDTO userLoginDTO) {
        if (!(PHONE.equals(userLoginDTO.getType())
                || EMAIL.equals(userLoginDTO.getType()))) {
            return R.errorMsg("请选择正确的登录方式进行登录~");
        }
        try {
            User user;
            if (PHONE.equals(userLoginDTO.getType())) {
                user = findByPhone(userLoginDTO.getUsername());
            } else {
                user = findByEmail(userLoginDTO.getUsername());
            }
            if (Objects.isNull(user)) {
                return R.errorCodeMsg(401,"用户名或密码错误，请重新登录~");
            }
            // 通过用户名和密码创建一个 Authentication 认证对象，实现类为 UsernamePasswordAuthenticationToken
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getName(), userLoginDTO.getPassword());

            //通过 AuthenticationManager（默认实现为ProviderManager）的authenticate方法验证 Authentication 对象
            Authentication authentication = authenticationManager.authenticate(authenticationToken);
            CustomUserDetail principal = (CustomUserDetail) authentication.getPrincipal();
            //将 Authentication 绑定到 SecurityContext
            SecurityContextHolder.getContext().setAuthentication(authentication);
            //生成Token
            String token = jwtTokenUtils.createToken(authentication);

            R<UserRole> userRoleRes = userRoleService.findByUserId(principal.getUser().getId());
            if (userRoleRes.isNotSuccess()) {
                return R.errorMsg(userRoleRes.getMsg());
            }
            UserLoginVO userLoginVO = new UserLoginVO();
            BeanUtils.copyProperties(principal.getUser(), userLoginVO);
            String roleName = Objects.requireNonNull(RoleEnum.getRole(userRoleRes.getData().getRoleId())).getRemark();
            userLoginVO.setRoleName(roleName);
            userLoginVO.setToken("Bearer " + token);
            return R.success(userLoginVO);

        } catch (BadCredentialsException authentication) {
            return R.errorCodeMsg(401,"用户名或密码错误，请重新登录~");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> register(UserRegisterDTO userRegisterDTO) {
        R<String> validateRsg = validateRegister(userRegisterDTO);
        if (STATUS_FAIL == validateRsg.getCode()) {
            return validateRsg;
        }
        User user = new User();
        BeanUtils.copyProperties(userRegisterDTO, user);
        // 密码加密处理
        user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
        int insert = userMapper.insert(user);
        if (insert < 1) {
            return R.errorMsg("系统异常，请稍后再试~");
        }
        R<String> userRoleRsg = userRoleService.add(user.getId(), userRegisterDTO.getRoleId());
        if (STATUS_FAIL == userRoleRsg.getCode()) {
            return userRoleRsg;
        }
        return R.successMsg("恭喜您添加账号成功！");
    }

    private R<String> validateRegister(UserRegisterDTO userRegisterDTO) {
        if (Objects.nonNull(findByName(userRegisterDTO.getName()))) {
            return R.errorMsg("该用户名已存在，请换一个用户名吧~");
        }
        if (Objects.nonNull(findByPhone(userRegisterDTO.getPhone()))) {
            return R.errorMsg("该手机号码已存在，请换一个手机号码吧~");
        }
        if (Objects.nonNull(findByEmail(userRegisterDTO.getEmail()))) {
            return R.errorMsg("该电子邮箱已存在，请换一个电子邮箱吧~");
        }
        return R.success();
    }

    private User findByEmail(String email) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(StringUtils.isNotBlank(email), User::getEmail, email);
        return userMapper.selectOne(lambdaQueryWrapper);
    }

    private User findByPhone(String phone) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(StringUtils.isNotBlank(phone), User::getPhone, phone);
        return userMapper.selectOne(lambdaQueryWrapper);
    }
}
